/*
 * Copyright (c) 2012 by Alecander Chudov <chudov@gmail.com>
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#ifndef	W5300_WRPR_H_INCLUDED
#define	W5300_WRPR_H_INCLUDED

extern "C" {
  #include "w5300.h"
}

class RgMR {
public:
  static const uint16_t DBW   = MR_DBW;
  static const uint16_t MPF   = MR_MPF;
  static const uint16_t WDF0  = MR_WDF(0);
  static const uint16_t WDF1  = MR_WDF(1);
  static const uint16_t RDH   = MR_RDH;
  static const uint16_t FS    = MR_FS;
  static const uint16_t RST   = MR_RST;
  static const uint16_t MT    = MR_MT;
  static const uint16_t PB    = MR_PB;
  static const uint16_t PPPOE = MR_PPPoE;
  static const uint16_t DBS   = MR_DBS;
  static const uint16_t IND   = MR_IND;
};

/*
class IR {
public:
  static const uint16_t CONFLICT = IR_IPCF;
  static const uint16_t UNREACH  = IR_DPUR;
  static const uint16_t PPPoE    = IR_PPPT;
  static const uint16_t FMTU		= IR_FMTU;
  static const uint16_t SOCK0    = IR_SnINT(0);
  static const uint16_t SOCK1    = IR_SnINT(1);
  static const uint16_t SOCK2    = IR_SnINT(2);
  static const uint16_t SOCK3    = IR_SnINT(3);
  static inline uint16_t SOCK(SOCKET ch) { return (IR_SnINT(ch)); };
};
*/

class RgSnMR {
public:
  static const uint16_t CLOSE  = Sn_MR_CLOSE;
  static const uint16_t TCP    = Sn_MR_TCP;
  static const uint16_t UDP    = Sn_MR_UDP;
  static const uint16_t IPRAW  = Sn_MR_IPRAW;
  static const uint16_t MACRAW = Sn_MR_MACRAW;
  static const uint16_t PPPOE  = Sn_MR_PPPoE;
  static const uint16_t ND     = Sn_MR_ND;
  static const uint16_t MULTI  = Sn_MR_MULTI;
  static const uint16_t IGMP   = Sn_MR_IGMPv;
  static const uint16_t MF     = Sn_MR_MF;
  static const uint16_t ALIGN  = Sn_MR_ALIGN;
};

enum SockCMD {
  Sock_OPEN      = Sn_CR_OPEN,
  Sock_LISTEN    = Sn_CR_LISTEN,
  Sock_CONNECT   = Sn_CR_CONNECT,
  Sock_DISCON    = Sn_CR_DISCON,
  Sock_CLOSE     = Sn_CR_CLOSE,
  Sock_SEND      = Sn_CR_SEND,
  Sock_SEND_MAC  = Sn_CR_SEND_MAC,
  Sock_SEND_KEEP = Sn_CR_SEND_KEEP,
  Sock_RECV      = Sn_CR_RECV,
  Sock_PCON		 = Sn_CR_PCON,
  Sock_PDISCON	 = Sn_CR_PDISCON,
  Sock_PCR		 = Sn_CR_PCR,
  Sock_PCN		 = Sn_CR_PCN,
  Sock_PCJ		 = Sn_CR_PCJ
};

/*class SnCmd {
public:
  static const uint16_t OPEN      = Sn_CR_OPEN;
  static const uint16_t LISTEN    = Sn_CR_LISTEN;
  static const uint16_t CONNECT   = Sn_CR_CONNECT;
  static const uint16_t DISCON    = Sn_CR_DISCON;
  static const uint16_t CLOSE     = Sn_CR_CLOSE;
  static const uint16_t SEND      = Sn_CR_SEND;
  static const uint16_t SEND_MAC  = Sn_CR_SEND_MAC;
  static const uint16_t SEND_KEEP = Sn_CR_SEND_KEEP;
  static const uint16_t RECV      = Sn_CR_RECV;
};
*/

class RgSnIR {
public:
  static const uint16_t P_RECV  = Sn_IR_PRECV;
  static const uint16_t P_FAIL  = Sn_IR_PFAIL;
  static const uint16_t P_NEXT  = Sn_IR_PNEXT;
  static const uint16_t SEND_OK = Sn_IR_SENDOK;
  static const uint16_t TIMEOUT = Sn_IR_TIMEOUT;
  static const uint16_t RECV    = Sn_IR_RECV;
  static const uint16_t DISCON  = Sn_IR_DISCON;
  static const uint16_t CON     = Sn_IR_CON;
};

class RgSnSR {
public:
  static const uint16_t CLOSED      = SOCK_CLOSED;
  static const uint16_t ARP         = SOCK_ARP;
  static const uint16_t INIT        = SOCK_INIT;
  static const uint16_t LISTEN      = SOCK_LISTEN;
  static const uint16_t SYNSENT     = SOCK_SYNSENT;
  static const uint16_t SYNRECV     = SOCK_SYNRECV;
  static const uint16_t ESTABLISHED = SOCK_ESTABLISHED;
  static const uint16_t FIN_WAIT    = SOCK_FIN_WAIT;
  static const uint16_t CLOSING     = SOCK_CLOSING;
  static const uint16_t TIME_WAIT   = SOCK_TIME_WAIT;
  static const uint16_t CLOSE_WAIT  = SOCK_CLOSE_WAIT;
  static const uint16_t LAST_ACK    = SOCK_LAST_ACK;
  static const uint16_t UDP         = SOCK_UDP;
  static const uint16_t IPRAW       = SOCK_IPRAW;
  static const uint16_t MACRAW      = SOCK_MACRAW;
  static const uint16_t PPPOE       = SOCK_PPPoE;
};

class IPPROTO {
public:
  static const uint8_t IP   = IPPROTO_IP;
  static const uint8_t ICMP = IPPROTO_ICMP;
  static const uint8_t IGMP = IPPROTO_IGMP;
  static const uint8_t GGP  = IPPROTO_GGP;
  static const uint8_t TCP  = IPPROTO_TCP;
  static const uint8_t PUP  = IPPROTO_PUP;
  static const uint8_t UDP  = IPPROTO_UDP;
  static const uint8_t IDP  = IPPROTO_IDP;
  static const uint8_t ND   = IPPROTO_ND;
  static const uint8_t RAW  = IPPROTO_RAW;
};

// dumb definitions
void setPATR(uint16_t);
void setUIPR(uint8_t*);
void setUPORTR(uint16_t);
void setSn_SSR(SOCKET, uint8_t);
uint16_t getSn_DPORTR(SOCKET);
void setSn_TX_FSR(SOCKET, uint32_t);
void setSn_RX_RSR(SOCKET, uint32_t);

class W5300Class {

public:
  void init();

  /**
   * @brief	This function is being used for copy the data form Receive buffer of the chip to application buffer.
   * 
   * It calculate the actual physical address where one has to read
   * the data from Receive buffer. Here also take care of the condition while it exceed
   * the Rx memory uper-bound of socket.
   */
  void read_data(SOCKET s, uint8_t * data, uint32_t len);
  
  /**
   * @brief	 This function is being called by send() and sendto() function also. 
   * 
   * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer
   * register. User should read upper byte first and lower byte later to get proper value.
   */
  void send_data_processing(SOCKET s, const uint8_t *data, uint32_t len);

  /**
   * @brief	This function is being called by recv() also.
   * 
   * This function read the Rx read pointer register
   * and after copy the data from receive buffer update the Rx write pointer register.
   * User should read upper byte first and lower byte later to get proper value.
   */
  void recv_data_processing(SOCKET s, uint8_t *data, uint32_t len);

  inline void setGatewayIp(uint8_t *_addr);
  inline void getGatewayIp(uint8_t *_addr);

  inline void setSubnetMask(uint8_t *_addr);
  inline void getSubnetMask(uint8_t *_addr);

  inline void setMACAddress(uint8_t * addr);
  inline void getMACAddress(uint8_t * addr);

  inline void setIPAddress(uint8_t * addr);
  inline void getIPAddress(uint8_t * addr);

  inline void setRetransmissionTime(uint16_t timeout);
  inline void setRetransmissionCount(uint8_t _retry);

  void execCmdSn(SOCKET s, SockCMD _cmd);
  
  uint32_t getTXFreeSize(SOCKET s);
  uint32_t getRXReceivedSize(SOCKET s);
  

  // W5300 Registers
  // ---------------
private:
//  static inline uint8_t write(uint16_t _addr, uint8_t _data) {
//	  IINCHIP_WRITE(addr, _data);
//  }
//  static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len);
//  static inline uint8_t read(uint16_t addr) {
//	  return (IINCHIP_READ(addr));
//  }
//  static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);
  
#define __GP_REGISTER8(name)		              \
  static inline void write##name(uint8_t _data) { \
    ::set##name(_data);	                          \
  }                                               \
  static inline uint8_t read##name() {            \
    return ::get##name();                         \
  }
#define __GP_REGISTER16(name)		              \
  static void write##name(uint16_t _data) {       \
    ::set##name(_data);			                  \
  }                                               \
  static uint16_t read##name() {                  \
    return ::get##name();                         \
  }
#define __GP_REGISTER_N(name)      \
  static void write##name(uint8_t *_buff) {		  \
    ::set##name(_buff); 					      \
  }                                               \
  static void read##name(uint8_t *_buff) {   	  \
	::get##name(_buff);					          \
  }

public:
  __GP_REGISTER16(MR)    	// Mode
  __GP_REGISTER_N(GAR) // Gateway IP address
  __GP_REGISTER_N(SUBR) // Subnet mask address
  __GP_REGISTER_N(SHAR) // Source MAC address
  __GP_REGISTER_N(SIPR) // Source IP address
  __GP_REGISTER16(IR)    // Interrupt
  __GP_REGISTER16(IMR)    // Interrupt Mask
  __GP_REGISTER16(RTR)    // Timeout address
  __GP_REGISTER8 (RCR)    // Retry count
  __GP_REGISTER16(PATR)    // Authentication type address in PPPoE mode
  __GP_REGISTER8 (PTIMER)    // PPP LCP Request Timer
  __GP_REGISTER8 (PMAGICR)    // PPP LCP Magic Number
  __GP_REGISTER_N(UIPR) // Unreachable IP address in UDP mode
  __GP_REGISTER16(UPORTR)    // Unreachable Port address in UDP mode
  
#undef __GP_REGISTER8
#undef __GP_REGISTER16
#undef __GP_REGISTER_N

  // W5300 Socket registers
  // ----------------------

private:
  static uint8_t write(uint16_t _addr, uint8_t _data);
  static uint8_t read(uint16_t addr);

  #define __SOCKET_REGISTER8(name)                             \
  static inline void write##name(SOCKET _s, uint8_t _data) { \
	  ::set##name(_s, _data);			                         \
  }                                                          \
  static inline uint8_t read##name(SOCKET _s) {              \
      return ::get##name(_s);	                                 \
  }

#define __SOCKET_REGISTER16(name)		                     \
  static inline void write##name(SOCKET _s, uint16_t _data){ \
      ::set##name(_s, _data);			                         \
  }                                                          \
  static inline uint16_t read##name(SOCKET _s) {             \
      return ::get##name(_s);	                                 \
  }

#define __SOCKET_REGISTER32(name)		                     \
  static inline void write##name(SOCKET _s, uint32_t _data){ \
    ::set##name(_s, _data);			                         \
  }                                                          \
  static inline uint32_t read##name(SOCKET _s) {             \
    return ::get##name(_s);	                                 \
  }

#define __SOCKET_REGISTER_N(name)		                     \
  static inline void write##name(SOCKET _s, uint8_t *_buff) {\
    ::set##name(_s, _buff);						             \
  }                                                          \
  static inline void read##name(SOCKET _s, uint8_t *_buff) { \
    ::get##name(_s, _buff);					                 \
  }
  
public:
  __SOCKET_REGISTER16(Sn_MR)        // Mode
  __SOCKET_REGISTER8 (Sn_CR)        // Command
  __SOCKET_REGISTER8 (Sn_IR)        // Interrupt
  __SOCKET_REGISTER8 (Sn_SSR)        // Status
  __SOCKET_REGISTER_N(Sn_DHAR)     // Destination Hardw Addr
  __SOCKET_REGISTER_N(Sn_DIPR)     // Destination IP Addr
  __SOCKET_REGISTER16(Sn_DPORTR)        // Destination Port
  __SOCKET_REGISTER16(Sn_MSSR)        // Max Segment Size
  __SOCKET_REGISTER8(Sn_PROTOR)        // Protocol in IP RAW Mode
  __SOCKET_REGISTER8(Sn_TOSR)        // IP TOS
  __SOCKET_REGISTER8(Sn_TTLR)        // IP TTL
  __SOCKET_REGISTER32(Sn_TX_FSR)        // TX Free Size
  __SOCKET_REGISTER32(Sn_RX_RSR)        // RX Free Size
  
#undef __SOCKET_REGISTER8
#undef __SOCKET_REGISTER16
#undef __SOCKET_REGISTER_N


private:

  static const uint8 SOCKETS = 8;
  static uint8 tx_mem_conf[SOCKETS]; // for setting TMSR regsiter
  static uint8 rx_mem_conf[SOCKETS]; // for setting RMSR regsiter

private:
  // dumb definitions
  void setSn_SSR(SOCKET _s, uint8_t _data);
  uint16_t getSn_DPORTR(SOCKET _s);
  void setSn_TX_FSR(SOCKET _s, uint32_t _data);
  void setSn_RX_RSR(SOCKET _s, uint32_t _data);

private:

};

extern W5300Class W5300;

void W5300Class::getGatewayIp(uint8_t *_addr) {
  readGAR(_addr);
}

void W5300Class::setGatewayIp(uint8_t *_addr) {
  writeGAR(_addr);
}

void W5300Class::getSubnetMask(uint8_t *_addr) {
  readSUBR(_addr);
}

void W5300Class::setSubnetMask(uint8_t *_addr) {
  writeSUBR(_addr);
}

void W5300Class::getMACAddress(uint8_t *_addr) {
  readSHAR(_addr);
}

void W5300Class::setMACAddress(uint8_t *_addr) {
  writeSHAR(_addr);
}

void W5300Class::getIPAddress(uint8_t *_addr) {
  readSIPR(_addr);
}

void W5300Class::setIPAddress(uint8_t *_addr) {
  writeSIPR(_addr);
}

void W5300Class::setRetransmissionTime(uint16_t _timeout) {
  writeRTR(_timeout);
}

void W5300Class::setRetransmissionCount(uint8_t _retry) {
  writeRCR(_retry);
}

#endif
